/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.editor;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.editor.client.EditorDelegate;
import com.google.gwt.editor.client.ValueAwareEditor;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.event.StoreAddEvent;
import com.sencha.gxt.data.shared.event.StoreAddEvent.StoreAddHandler;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent.StoreRemoveHandler;


/**
 * List store editor that keeps removed items in an removedItems list for post-processing purpose.<br>
 * <br>
 * 
 * Binds a {@link ListStore} to a {@link List} property in the edited model object. When flushed, the list property will
 * contain the current visible items in the store and all of the changes made to the items.
 * <p>
 * If bound to a null value, no changes will be made when flushed.
 * </p>
 * <p>
 * This will not collect subeditor paths, as the Store and whatever it is bound to are not treated like sub-editors.
 * </p>
 * 
 * 
 * 
 * <pre>
 * <code>
 * public interface MyData {
 *   List&lt;Person> getPeople();
 * }
 * public class MyDataEditor implements Editor&lt;MyData>{
 *   private ListStore&lt;Person> peopleStore = new ListStore&lt;Person>(...);
 *   private Grid&lt;Person> peopleGrid;
 *   
 *   ListStoreEditor&lt;Person> people;//declare a sub editor
 *   
 *   public MyDataEditor() {
 *     peopleStore = new ListStore&lt;Person>(...);         //create the real store
 *     people = new ListStoreEditor&lt;Person>(peopleStore);//map to it with an editor
 *     
 *     grid = new Grid&lt;Person>(peopleStore, ...);
 *   }
 * }
 * </code>
 * </pre>
 * 
 * @author rdupuis
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <T>
 */
public class ListStoreRemovableEditor<T> implements ValueAwareEditor<List<T>>
{
	private final ListStore<T> store;
	private final List<T> removedItems = new ArrayList<T>();
	private final List<T> originalModels = new ArrayList<T>();
	private List<T> beanList;

	/**
	 * @param keyProvider
	 */
	public ListStoreRemovableEditor(final ModelKeyProvider<T> keyProvider)
	{
		this.store = new ListStore<T>(keyProvider);
		getStore().addStoreRemoveHandler(new StoreRemoveHandler<T>()
		{
			@Override
			public void onRemove(final StoreRemoveEvent<T> event)
			{
				removedItems.add(event.getItem());
			}
		});
		getStore().addStoreAddHandler(new StoreAddHandler<T>()
		{
			@Override
			public void onAdd(final StoreAddEvent<T> event)
			{
				removedItems.removeAll(event.getItems());
			}
		});
	}

	@Override
	public void setValue(final List<T> value)
	{
		// here a store.clear() would lead to a call to leyout() method on GridView, which causes further size problems when the grid is not visible
		final int storeSize = store.size();
		for (int i = 0; i < storeSize; i++)
		{
			store.remove(0);
		}
		//--------
		if (value != null && value.size() > 0)
		{
			store.addAll(value);
		}

		this.originalModels.clear();
		if (value != null)
		{
			this.originalModels.addAll(value);
		}
		removedItems.clear();

		this.beanList = value;
	}

	@Override
	public void onPropertyChange(final String... paths)
	{
		// as ListEditor, noop
	}

	@Override
	public void setDelegate(final EditorDelegate<List<T>> delegate)
	{
		// ignore for now, this could be used to pass errors into the view
	}

	@Override
	public void flush()
	{
		// make any modifications to the models themselves
		store.commitChanges();

		// flush out the contents of the list, so structural changes are made as
		// well
		if (beanList != null)
		{
			beanList.clear();
			beanList.addAll(store.getAll());
		}
	}

	/**
	 * @return removed items
	 */
	public List<T> getRemovedItems()
	{
		return removedItems;
	}

	/**
	 * @return the models
	 */
	public List<T> getOriginalModels()
	{
		return originalModels;
	}

	/**
	 * @return the store
	 */
	public ListStore<T> getStore()
	{
		return store;
	}
}
